home *** CD-ROM | disk | FTP | other *** search
/ MacWorld 2003 August / MW 8 2003 CD1.iso / Inside Macworld / Product News / gimp-1.2.4.sit / gimp-1.2.4 / plug-ins / sel2path / sel2path.c < prev    next >
Encoding:
C/C++ Source or Header  |  2002-08-26  |  17.0 KB  |  614 lines

  1. /*
  2.  * Copyright (C) 1995 Spencer Kimball and Peter Mattis
  3.  *
  4.  * This is a plug-in for the GIMP.
  5.  *
  6.  * Plugin to convert a selection to a path.
  7.  *
  8.  * Copyright (C) 1999 Andy Thomas  alt@gimp.org
  9.  *
  10.  * This program is free software; you can redistribute it and/or modify
  11.  * it under the terms of the GNU General Public License as published by
  12.  * the Free Software Foundation; either version 2 of the License, or
  13.  * (at your option) any later version.
  14.  *
  15.  * This program is distributed in the hope that it will be useful,
  16.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  17.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  18.  * GNU General Public License for more details.
  19.  *
  20.  * You should have received a copy of the GNU General Public License
  21.  * along with this program; if not, write to the Free Software
  22.  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  23.  * 
  24.  */
  25.  
  26. /* Change log:-
  27.  * 0.1 First version.
  28.  */
  29.  
  30. #include "config.h"
  31.  
  32. #include <glib.h>
  33.  
  34. #include <stdio.h>
  35. #include <stdlib.h>
  36. #include <string.h>
  37. #include <math.h>
  38.  
  39. #include <gtk/gtk.h>
  40.  
  41. #include <libgimp/gimp.h>
  42. #include <libgimp/gimpui.h>
  43.  
  44. #include "global.h"
  45. #include "types.h"
  46. #include "pxl-outline.h"
  47. #include "fit.h"
  48. #include "spline.h"
  49. #include "sel2path.h"
  50.  
  51. #include "libgimp/stdplugins-intl.h"
  52.  
  53.  
  54. #define MID_POINT 127
  55.  
  56. /***** Magic numbers *****/
  57.  
  58. /* Variables set in dialog box */
  59.  
  60. static void      query  (void);
  61. static void      run    (gchar       *name,
  62.              gint         nparams,
  63.              GimpParam   *param,
  64.              gint        *nreturn_vals,
  65.              GimpParam  **return_vals);
  66.  
  67. static gint      sel2path_dialog         (SELVALS   *sels);
  68. static void      sel2path_ok_callback    (GtkWidget *widget, 
  69.                       gpointer   data);
  70. static void      sel2path_reset_callback (GtkWidget *widget,
  71.                       gpointer   data);
  72. static void      dialog_print_selVals    (SELVALS   *sels);
  73. gboolean         do_sel2path             (gint32     drawable_ID, 
  74.                       gint32     image_ID);
  75.  
  76.  
  77. GimpPlugInInfo PLUG_IN_INFO =
  78. {
  79.   NULL,    /* init_proc */
  80.   NULL,    /* quit_proc */
  81.   query,   /* query_proc */
  82.   run,     /* run_proc */
  83. };
  84.  
  85. static gint    sel_x1, sel_y1, sel_x2, sel_y2;
  86. static gint    has_sel, sel_width, sel_height;
  87. static SELVALS selVals;
  88. GimpPixelRgn   selection_rgn;
  89. gboolean       retVal = TRUE;  /* Toggle if cancle button clicked */
  90.  
  91. MAIN ()
  92.  
  93. static void
  94. query_2 (void)
  95. {
  96.   static GimpParamDef args[] =
  97.   {
  98.     { GIMP_PDB_INT32,    "run_mode",                    "Interactive, non-interactive" },
  99.     { GIMP_PDB_IMAGE,    "image",                       "Input image (unused)" },
  100.     { GIMP_PDB_DRAWABLE, "drawable",                    "Input drawable" }, 
  101.     { GIMP_PDB_FLOAT,    "align_threshold",             "align_threshold"},
  102.     { GIMP_PDB_FLOAT,    "corner_always_threshold",     "corner_always_threshold"},
  103.     { GIMP_PDB_INT8,     "corner_surround",             "corner_surround"},
  104.     { GIMP_PDB_FLOAT,    "corner_threshold",            "corner_threshold"},
  105.     { GIMP_PDB_FLOAT,    "error_threshold",             "error_threshold"},
  106.     { GIMP_PDB_INT8,     "filter_alternative_surround", "filter_alternative_surround"},
  107.     { GIMP_PDB_FLOAT,    "filter_epsilon",              "filter_epsilon"},
  108.     { GIMP_PDB_INT8,     "filter_iteration_count",      "filter_iteration_count"},
  109.     { GIMP_PDB_FLOAT,    "filter_percent",              "filter_percent"},
  110.     { GIMP_PDB_INT8,     "filter_secondary_surround",   "filter_secondary_surround"},
  111.     { GIMP_PDB_INT8,     "filter_surround",             "filter_surround"},
  112.     { GIMP_PDB_INT8,     "keep_knees",                  "{1-Yes, 0-No}"},
  113.     { GIMP_PDB_FLOAT,    "line_reversion_threshold",    "line_reversion_threshold"},
  114.     { GIMP_PDB_FLOAT,    "line_threshold",              "line_threshold"},
  115.     { GIMP_PDB_FLOAT,    "reparameterize_improvement",  "reparameterize_improvement"},
  116.     { GIMP_PDB_FLOAT,    "reparameterize_threshold",    "reparameterize_threshold"},
  117.     { GIMP_PDB_FLOAT,    "subdivide_search",            "subdivide_search"},
  118.     { GIMP_PDB_INT8,     "subdivide_surround",          "subdivide_surround"},
  119.     { GIMP_PDB_FLOAT,    "subdivide_threshold",         "subdivide_threshold"},
  120.     { GIMP_PDB_INT8,     "tangent_surround",            "tangent_surround"},
  121.   };
  122.   static GimpParamDef *return_vals = NULL;
  123.   static int nargs = sizeof (args) / sizeof (args[0]);
  124.   static int nreturn_vals = 0;
  125.  
  126.   gimp_install_procedure ("plug_in_sel2path_advanced",
  127.               "Converts a selection to a path (with advanced user menu)",
  128.               "Converts a selection to a path (with advanced user menu)",
  129.               "Andy Thomas",
  130.               "Andy Thomas",
  131.               "1999",
  132.               NULL,
  133.               "RGB*, INDEXED*, GRAY*",
  134.               GIMP_PLUGIN,
  135.               nargs, nreturn_vals,
  136.               args, return_vals);
  137. }
  138.  
  139. static void
  140. query (void)
  141. {
  142.   static GimpParamDef args[] =
  143.   {
  144.     { GIMP_PDB_INT32,    "run_mode", "Interactive, non-interactive" },
  145.     { GIMP_PDB_IMAGE,    "image",    "Input image (unused)" },
  146.     { GIMP_PDB_DRAWABLE, "drawable", "Input drawable" },
  147.   };
  148.   static GimpParamDef *return_vals = NULL;
  149.   static int nargs = sizeof (args) / sizeof (args[0]);
  150.   static int nreturn_vals = 0;
  151.  
  152.   INIT_I18N();
  153.  
  154.   gimp_install_procedure ("plug_in_sel2path",
  155.               "Converts a selection to a path",
  156.               "Converts a selection to a path",
  157.               "Andy Thomas",
  158.               "Andy Thomas",
  159.               "1999",
  160.               N_("<Image>/Select/To Path"),
  161.               "RGB*, INDEXED*, GRAY*",
  162.               GIMP_PLUGIN,
  163.               nargs, nreturn_vals,
  164.               args, return_vals);
  165.  
  166.   query_2 ();
  167. }
  168.  
  169. static void
  170. run (gchar       *name,
  171.      gint         nparams,
  172.      GimpParam   *param,
  173.      gint        *nreturn_vals,
  174.      GimpParam  **return_vals)
  175. {
  176.   static GimpParam   values[1];
  177.   GimpDrawable      *drawable;
  178.   gint32             drawable_ID;
  179.   gint32             image_ID;
  180.   GimpRunModeType    run_mode;
  181.   GimpPDBStatusType  status    = GIMP_PDB_SUCCESS;
  182.   gboolean           no_dialog = FALSE;
  183.  
  184.   run_mode = param[0].data.d_int32;
  185.  
  186.   if(!strcmp(name,"plug_in_sel2path")) 
  187.     {
  188.       no_dialog = TRUE;
  189.       INIT_I18N();
  190.     } 
  191.   else
  192.     INIT_I18N_UI();
  193.  
  194.   *nreturn_vals = 1;
  195.   *return_vals = values;
  196.  
  197.   values[0].type = GIMP_PDB_STATUS;
  198.   values[0].data.d_status = status;
  199.  
  200.   drawable_ID = param[2].data.d_drawable;
  201.   drawable = gimp_drawable_get (drawable_ID);
  202.  
  203.   image_ID = gimp_drawable_image_id (drawable_ID);
  204.  
  205.   if (gimp_selection_is_empty (image_ID))
  206.     {
  207.       g_message (_("No selection to convert"));
  208.       gimp_drawable_detach (drawable);
  209.       return;
  210.     }
  211.  
  212.   fit_set_default_params(&selVals);
  213.   
  214.   if (!no_dialog)
  215.     {
  216.       switch (run_mode)
  217.     {
  218.     case GIMP_RUN_INTERACTIVE:
  219.       if (gimp_get_data_size ("plug_in_sel2path_advanced") > 0)
  220.         {
  221.           gimp_get_data ("plug_in_sel2path_advanced", &selVals);
  222.         }
  223.  
  224.       if (!sel2path_dialog (&selVals))
  225.         {
  226.           gimp_drawable_detach (drawable);
  227.           return;
  228.         }
  229.       /* Get the current settings */
  230.       fit_set_params (&selVals);
  231.       break;
  232.       
  233.     case GIMP_RUN_NONINTERACTIVE:
  234.       if (nparams != 23)
  235.         status = GIMP_PDB_CALLING_ERROR;
  236.       
  237.       if (status == GIMP_PDB_SUCCESS) 
  238.         {
  239.           selVals.align_threshold             =  param[3].data.d_float;
  240.           selVals.corner_always_threshold     =  param[4].data.d_float;
  241.           selVals.corner_surround             =  param[5].data.d_int8;
  242.           selVals.corner_threshold            =  param[6].data.d_float;
  243.           selVals.error_threshold             =  param[7].data.d_float;
  244.           selVals.filter_alternative_surround =  param[8].data.d_int8;
  245.           selVals.filter_epsilon              =  param[9].data.d_float;
  246.           selVals.filter_iteration_count      = param[10].data.d_int8;
  247.           selVals.filter_percent              = param[11].data.d_float;
  248.           selVals.filter_secondary_surround   = param[12].data.d_int8;
  249.           selVals.filter_surround             = param[13].data.d_int8;
  250.           selVals.keep_knees                  = param[14].data.d_int8;
  251.           selVals.line_reversion_threshold    = param[15].data.d_float;
  252.           selVals.line_threshold              = param[16].data.d_float;
  253.           selVals.reparameterize_improvement  = param[17].data.d_float;
  254.           selVals.reparameterize_threshold    = param[18].data.d_float;
  255.           selVals.subdivide_search            = param[19].data.d_float;
  256.           selVals.subdivide_surround          = param[20].data.d_int8;
  257.           selVals.subdivide_threshold         = param[21].data.d_float;
  258.           selVals.tangent_surround            = param[22].data.d_int8;
  259.           fit_set_params(&selVals);
  260.       }
  261.  
  262.       break;
  263.       
  264.     case GIMP_RUN_WITH_LAST_VALS:
  265.       if(gimp_get_data_size ("plug_in_sel2path_advanced") > 0)
  266.         {
  267.           gimp_get_data ("plug_in_sel2path_advanced", &selVals);
  268.           
  269.           /* Set up the last values */
  270.           fit_set_params (&selVals);
  271.         }
  272.  
  273.       break;
  274.       
  275.     default:
  276.       break;
  277.     }
  278.     }
  279.  
  280.   do_sel2path (drawable_ID,image_ID);
  281.   values[0].data.d_status = status;
  282.  
  283.   if (status == GIMP_PDB_SUCCESS)
  284.     {
  285.       dialog_print_selVals(&selVals);
  286.       if (run_mode == GIMP_RUN_INTERACTIVE && !no_dialog)
  287.     gimp_set_data ("plug_in_sel2path_advanced", &selVals, sizeof(SELVALS));
  288.     }
  289.  
  290.   gimp_drawable_detach (drawable);
  291. }
  292.  
  293. static void
  294. dialog_print_selVals (SELVALS *sels)
  295. {
  296. #if 0
  297.   printf ("selVals.align_threshold %g\n",             selVals.align_threshold);
  298.   printf ("selVals.corner_always_threshol %g\n",      selVals.corner_always_threshold);
  299.   printf ("selVals.corner_surround %g\n",             selVals.corner_surround);
  300.   printf ("selVals.corner_threshold %g\n",            selVals.corner_threshold);
  301.   printf ("selVals.error_threshold %g\n",             selVals.error_threshold);
  302.   printf ("selVals.filter_alternative_surround %g\n", selVals.filter_alternative_surround);
  303.   printf ("selVals.filter_epsilon %g\n",              selVals.filter_epsilon);
  304.   printf ("selVals.filter_iteration_count %g\n",      selVals.filter_iteration_count);
  305.   printf ("selVals.filter_percent %g\n",              selVals.filter_percent);
  306.   printf ("selVals.filter_secondary_surround %g\n",   selVals.filter_secondary_surround);
  307.   printf ("selVals.filter_surround %g\n",             selVals.filter_surround);
  308.   printf ("selVals.keep_knees %d\n",                  selVals.keep_knees);
  309.   printf ("selVals.line_reversion_threshold %g\n",    selVals.line_reversion_threshold);
  310.   printf ("selVals.line_threshold %g\n",              selVals.line_threshold);
  311.   printf ("selVals.reparameterize_improvement %g\n",  selVals.reparameterize_improvement);
  312.   printf ("selVals.reparameterize_threshold %g\n",    selVals.reparameterize_threshold);
  313.   printf ("selVals.subdivide_search %g\n"             selVals.subdivide_search);
  314.   printf ("selVals.subdivide_surround %g\n",          selVals.subdivide_surround);
  315.   printf ("selVals.subdivide_threshold %g\n",         selVals.subdivide_threshold);
  316.   printf ("selVals.tangent_surround %g\n",            selVals.tangent_surround);
  317. #endif /* 0 */
  318. }
  319.  
  320. /* Build the dialog up. This was the hard part! */
  321. static gint
  322. sel2path_dialog (SELVALS *sels)
  323. {
  324.   GtkWidget *dlg;
  325.   GtkWidget *table;
  326.  
  327.   retVal = FALSE;
  328.  
  329.   gimp_ui_init ("sel2path", FALSE);
  330.  
  331.   dlg = gimp_dialog_new (_("Selection To Path Advanced Settings"), "sel2path",
  332.              gimp_standard_help_func, "filters/sel2path.html",
  333.              GTK_WIN_POS_MOUSE,
  334.              FALSE, TRUE, FALSE,
  335.  
  336.              _("OK"), sel2path_ok_callback,
  337.              NULL, NULL, NULL, TRUE, FALSE,
  338.  
  339.              _("Reset"), sel2path_reset_callback,
  340.              NULL, NULL, NULL, FALSE, FALSE,
  341.  
  342.              _("Cancel"), gtk_widget_destroy,
  343.              NULL, 1, NULL, FALSE, TRUE,
  344.  
  345.              NULL);
  346.  
  347.   gtk_signal_connect (GTK_OBJECT (dlg), "destroy",
  348.               GTK_SIGNAL_FUNC (gtk_main_quit),
  349.               NULL);
  350.   
  351.   table = dialog_create_selection_area(sels);
  352.   gtk_container_set_border_width (GTK_CONTAINER (table), 6);
  353.   gtk_container_add (GTK_CONTAINER (GTK_DIALOG (dlg)->vbox), table);
  354.   gtk_widget_show (table);
  355.  
  356.   gtk_widget_show (dlg);
  357.  
  358.   gtk_main ();
  359.  
  360.   return retVal;
  361. }
  362.  
  363. static void
  364. sel2path_ok_callback (GtkWidget *widget,
  365.               gpointer   data)
  366. {
  367.   gtk_widget_destroy (GTK_WIDGET (data));
  368.   retVal = TRUE;
  369. }
  370.  
  371. static void
  372. sel2path_reset_callback (GtkWidget *widget,
  373.              gpointer   data)
  374. {
  375.   reset_adv_dialog ();
  376.   fit_set_params (&selVals);
  377. }
  378.  
  379. guchar
  380. sel_pixel_value (gint row, 
  381.          gint col)
  382. {
  383.   guchar ret;
  384.  
  385.   if (col > sel_width || row > sel_height)
  386.     {
  387.       g_warning ("sel_pixel_value [%d,%d] out of bounds", col, row);
  388.       return 0;
  389.     }
  390.  
  391.   gimp_pixel_rgn_get_pixel(&selection_rgn,&ret,col+sel_x1,row+sel_y1);
  392.  
  393.   return ret;
  394. }
  395.  
  396. gboolean
  397. sel_pixel_is_white (gint row, 
  398.             gint col)
  399. {
  400.   if (sel_pixel_value (row, col) < MID_POINT)
  401.     return TRUE;
  402.   else
  403.     return FALSE;
  404. }
  405.  
  406. gint 
  407. sel_get_width (void)
  408. {
  409.   return sel_width;
  410. }
  411.  
  412. gint
  413. sel_get_height (void)
  414. {
  415.   return sel_height;
  416. }
  417.  
  418. gint 
  419. sel_valid_pixel (gint row, 
  420.          gint col)
  421. {
  422.   return (0 <= (row) && (row) < sel_get_height ()
  423.       && 0 <= (col) && (col) < sel_get_width ());
  424. }
  425.  
  426. void
  427. gen_anchor (gdouble  *p,
  428.         gdouble   x,
  429.         gdouble   y,
  430.         gboolean  is_newcurve)
  431. {
  432. /*   printf("TYPE: %s X: %d Y: %d\n",  */
  433. /*      (is_newcurve)?"3":"1",  */
  434. /*      sel_x1+(int)RINT(x),  */
  435. /*      sel_y1 + sel_height - (int)RINT(y)+1); */
  436.  
  437.   *p++ = (sel_x1 + (gint)RINT(x));
  438.   *p++ = sel_y1 + sel_height - (gint)RINT(y) + 1;
  439.   *p++ = (is_newcurve) ? 3.0 : 1.0;
  440. }
  441.   
  442.  
  443. void  
  444. gen_control (gdouble *p,
  445.          gdouble  x,
  446.          gdouble  y)
  447. {
  448. /*   printf("TYPE: 2 X: %d Y: %d\n",  */
  449. /*       sel_x1+(int)RINT(x),  */
  450. /*       sel_y1 + sel_height - (int)RINT(y)+1);  */
  451.  
  452.   *p++ = sel_x1 + (gint)RINT(x);
  453.   *p++ = sel_y1 + sel_height - (gint)RINT(y) + 1;
  454.   *p++ = 2.0;
  455.  
  456. }
  457.  
  458. void
  459. do_points (spline_list_array_type in_splines,
  460.        gint32                 image_ID)
  461. {
  462.   unsigned this_list;
  463.   gint seg_count = 0;
  464.   gint point_count = 0;
  465.   gdouble last_x,last_y;
  466.   gdouble *parray;
  467.   gdouble *cur_parray;
  468.   gint path_point_count;
  469.  
  470.   for (this_list = 0; this_list < SPLINE_LIST_ARRAY_LENGTH (in_splines);
  471.        this_list++)
  472.     {
  473.       spline_list_type in_list = SPLINE_LIST_ARRAY_ELT (in_splines, this_list);
  474.       /* Ignore single points that are on their own */
  475.       if(SPLINE_LIST_LENGTH (in_list) < 2)
  476.     continue;
  477.       point_count += SPLINE_LIST_LENGTH (in_list);
  478.     }
  479.  
  480. /*   printf("Name SEL2PATH\n"); */
  481. /*   printf("#POINTS: %d\n",point_count*3); */
  482. /*   printf("CLOSED: 1\n"); */
  483. /*   printf("DRAW: 0\n"); */
  484. /*   printf("STATE: 4\n"); */
  485.  
  486.   path_point_count = point_count*9;
  487.   cur_parray = (gdouble *)g_new0(gdouble ,point_count*9);
  488.   parray = cur_parray;
  489.  
  490.   point_count = 0;
  491.  
  492.   for (this_list = 0; this_list < SPLINE_LIST_ARRAY_LENGTH (in_splines);
  493.        this_list++)
  494.     {
  495.       unsigned this_spline;
  496.       spline_list_type in_list = SPLINE_LIST_ARRAY_ELT (in_splines, this_list);
  497.       
  498. /*       if(seg_count > 0 && point_count > 0)   */
  499. /*     gen_anchor(last_x,last_y,0);   */
  500.       
  501.       point_count = 0;
  502.  
  503.       /* Ignore single points that are on their own */
  504.       if(SPLINE_LIST_LENGTH (in_list) < 2)
  505.       continue;
  506.       
  507.       for (this_spline = 0; this_spline < SPLINE_LIST_LENGTH (in_list);
  508.        this_spline++)
  509.     {
  510.       spline_type s = SPLINE_LIST_ELT (in_list, this_spline);
  511.       
  512.       if (SPLINE_DEGREE (s) == LINEAR)
  513.         {
  514.           gen_anchor (cur_parray, 
  515.               START_POINT (s).x, START_POINT (s).y, 
  516.               seg_count && !point_count);
  517.           cur_parray += 3;
  518.           gen_control (cur_parray, START_POINT (s).x, START_POINT (s).y);
  519.           cur_parray += 3;
  520.           gen_control (cur_parray,END_POINT (s).x, END_POINT (s).y);
  521.           cur_parray += 3;
  522.           last_x = END_POINT (s).x;
  523.           last_y = END_POINT (s).y;
  524.         }
  525.       else if (SPLINE_DEGREE (s) == CUBIC)
  526.         {
  527.           gen_anchor (cur_parray,
  528.               START_POINT (s).x, START_POINT (s).y,
  529.               seg_count && !point_count);
  530.           cur_parray += 3;
  531.           gen_control (cur_parray,CONTROL1 (s).x, CONTROL1 (s).y);
  532.           cur_parray += 3;
  533.           gen_control (cur_parray,CONTROL2 (s).x, CONTROL2 (s).y);
  534.           cur_parray += 3;
  535.           last_x = END_POINT (s).x;
  536.           last_y = END_POINT (s).y;
  537.         }
  538.       else
  539.         g_warning ("print_spline: strange degree (%d)", SPLINE_DEGREE (s));
  540.       
  541.       point_count++;
  542.     }
  543.       seg_count++;
  544.     }
  545.  
  546.    gimp_path_set_points (image_ID,
  547.              "selection_to_path", 
  548.                          1, 
  549.                          path_point_count,
  550.                          parray);
  551. }
  552.  
  553.  
  554. gboolean
  555. do_sel2path (gint32 drawable_ID,
  556.          gint32 image_ID)
  557. {
  558.   gint32      selection_ID;
  559.   GimpDrawable  *sel_drawable;
  560.   pixel_outline_list_type olt;
  561.   spline_list_array_type splines;
  562.  
  563.   gimp_selection_bounds (image_ID, &has_sel, 
  564.              &sel_x1, &sel_y1, &sel_x2, &sel_y2);
  565.  
  566.   sel_width  = sel_x2 - sel_x1;
  567.   sel_height = sel_y2 - sel_y1;
  568.  
  569.   /* Now get the selection channel */
  570.  
  571.   selection_ID = gimp_image_get_selection(image_ID);
  572.  
  573.   if (selection_ID < 0)
  574.     {
  575.       g_warning ("gimp_image_get_selection failed");
  576.       return FALSE;
  577.     }
  578.  
  579.   sel_drawable = gimp_drawable_get (selection_ID);
  580.  
  581.   if (gimp_drawable_bpp (selection_ID) != 1)
  582.     {
  583.       g_warning ("Internal error. Selection bpp > 1");
  584.       return FALSE;
  585.     }
  586.  
  587.   gimp_pixel_rgn_init (&selection_rgn, sel_drawable,
  588.                sel_x1, sel_y1, sel_width, sel_height,
  589.                FALSE, FALSE);
  590.  
  591.   gimp_tile_cache_ntiles (2 * (sel_drawable->width + gimp_tile_width () - 1) / 
  592.               gimp_tile_width ());
  593.  
  594.   olt = find_outline_pixels ();
  595.  
  596.   splines = fitted_splines (olt);
  597.  
  598.   gimp_selection_none (image_ID);
  599.   gimp_displays_flush ();
  600.  
  601.   do_points (splines, image_ID);
  602.  
  603.   return TRUE;
  604. }
  605.  
  606. void
  607. safe_free (address *item)
  608. {
  609.   g_return_if_fail (item != NULL);
  610.   
  611.   g_free (*item);
  612.   *item = NULL;
  613. }
  614.